package com.hzw.saas.web.hbox.controller;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.hzw.saas.api.storage.IResourceFileService;
import com.hzw.saas.api.storage.IResourceFolderService;
import com.hzw.saas.api.storage.IResourceService;
import com.hzw.saas.api.storage.bo.ResourceFileBO;
import com.hzw.saas.api.storage.bo.ResourceFolderStructBO;
import com.hzw.saas.api.storage.bo.ResourcesBO;
import com.hzw.saas.api.storage.enums.ResourceAuthEnum;
import com.hzw.saas.api.storage.enums.ResourceStatusEnum;
import com.hzw.saas.api.storage.exception.FileConflictException;
import com.hzw.saas.common.config.annotation.SysLog;
import com.hzw.saas.common.config.bo.BaseBO;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.query.LogicQuery;
import com.hzw.saas.common.security.utils.SecurityUtils;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.enums.AssertEnum;
import com.hzw.saas.common.util.enums.ConflictModeEnum;
import com.hzw.saas.service.storage.annotation.FolderAuthBefore;
import com.hzw.saas.service.storage.util.StorageUtil;
import com.hzw.saas.web.hbox.constants.HBoxConsts;
import com.hzw.saas.web.hbox.dto.HBoxFileDTO;
import com.hzw.saas.web.hbox.dto.HBoxFolderDTO;
import com.hzw.saas.web.hbox.dto.HBoxFolderPostDTO;
import com.hzw.saas.web.hbox.param.HBoxFolderDeleteParam;
import com.hzw.saas.web.hbox.param.HBoxFolderGetParam;
import com.hzw.saas.web.hbox.param.HBoxFolderPostParam;
import com.hzw.saas.web.hbox.param.HBoxFolderPutParam;
import com.hzw.saas.web.hbox.util.HBoxUtil;
import io.swagger.annotations.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 文件目录控制器
 *
 * @author zzl
 * @since 02/26/2021
 */
// @Controller
@RequiredArgsConstructor
@Slf4j
// @Api(tags = "资源管理/目录管理")
public class HBoxFolderController {

    private final IResourceService resourceService;
    private final IResourceFileService fileService;
    private final IResourceFolderService folderService;

    @GetMapping("/resource/folders/info")
    @FolderAuthBefore(folderId = "#param.folderId", auth = ResourceAuthEnum.OWN)
    @SysLog
    @ApiOperation(value = "获取目录", notes = "获取目录及其下资源列表")
    @ApiOperationSort(1)
    public ResponseEntity<HBoxFolderDTO> getFolders(@Validated HBoxFolderGetParam param) {
        String userId = SecurityUtils.getUser().getUserId();
        param.setFolderId(HBoxUtil.parseFolderId(userId, param.getFolderId()));
        if (Objects.isNull(param.getPathLevel()))
            param.setPathLevel(-1);
        ResourceStatusEnum statusEnum = ResourceStatusEnum.instance(param.getStatus());
        LogicQuery<Integer> query = Objects.isNull(statusEnum) ? null : LogicQuery.eq(statusEnum.getCode());
        ResourcesBO subResources = resourceService.listSubResources(param.getFolderId(),
            query, param.getPathLevel(), true);
        List<HBoxFolderDTO> subFolders = MapperUtil.nf().mapAsList(subResources.getFolders(), HBoxFolderDTO.class);
        List<HBoxFileDTO> subFiles = MapperUtil.nf().mapAsList(subResources.getFiles(), HBoxFileDTO.class);
        HBoxFolderDTO hboxFolder;
        if (StorageUtil.isRootId(param.getFolderId())) {
            hboxFolder = new HBoxFolderDTO();
            hboxFolder.setFolderId(HBoxConsts.ROOT_FOLDER_ID);
            hboxFolder.setOwnerId(userId);
            hboxFolder.setStatus(ResourceStatusEnum.NORMAL.getCode());
            hboxFolder.setName(HBoxConsts.ROOT_FOLDER_NAME);
        } else {
            ResourceFolderStructBO resourceFolder = folderService.getFolder(param.getFolderId(), AssertEnum.FOUND);
            hboxFolder = MapperUtil.nf().map(resourceFolder, HBoxFolderDTO.class);
        }
        hboxFolder.setSubFiles(subFiles);
        hboxFolder.setSubFolders(subFolders);
        return ResponseEntity.ok(hboxFolder);
    }

    @PostMapping("/resource/folders")
    @FolderAuthBefore(folderId = "#param.folderId", auth = ResourceAuthEnum.READ)
    @SysLog(operation = "创建目录", mode = 3)
    @ApiOperation(value = "创建目录", notes = "创建目录，支持多级目录创建")
    @ApiOperationSort(2)
    public ResponseEntity<List<HBoxFolderPostDTO>> createFolders(@Validated @RequestBody HBoxFolderPostParam param) {
        String userId = SecurityUtils.getUser().getUserId();
        param.setFolderId(HBoxUtil.parseFolderId(userId, param.getFolderId()));
        List<ResourceFolderStructBO> folders = folderService.createFoldersByPath(param.getFolderId(), param.getPath());
        List<HBoxFolderDTO> hBoxFolderDTOS = MapperUtil.nf().mapAsList(folders, HBoxFolderDTO.class);
        List<HBoxFolderPostDTO> result = MapperUtil.nf().mapAsList(hBoxFolderDTOS, HBoxFolderPostDTO.class);
        return ResponseEntity.ok(result);
    }

    @PutMapping("/resource/folder/info")
    @FolderAuthBefore(folderId = "#param.folderId", auth = ResourceAuthEnum.OWN)
    @SysLog(operation = "更新目录", mode = 3)
    @ApiOperation(value = "更新目录", notes = "更新目录信息，如父目录，目录名等")
    @ApiResponses({
        @ApiResponse(code = 200, message = "请求成功"),
        @ApiResponse(code = 400, message = "请求参数异常/目录信息不存在"),
        @ApiResponse(code = 409, message = "恢复至的目录存在同名目录"),
        @ApiResponse(code = 500, message = "服务器内部错误")
    })
    @ApiOperationSort(3)
    public ResponseEntity<Void> updateFolder(@Validated @RequestBody HBoxFolderPutParam param) {
        String userId = SecurityUtils.getUser().getUserId();
        param.setFolderId(HBoxUtil.parseFolderId(userId, param.getFolderId()));
        try {
            if (StrUtil.isNotBlank(param.getParentId())) {
                param.setParentId(HBoxUtil.parseFolderId(userId, param.getParentId()));
                String newName = StrUtil.isBlank(param.getName()) ? null : param.getName();
                folderService.moveFolder(param.getFolderId(), param.getParentId(), newName, ConflictModeEnum.THROW_ERR);
            } else if (StrUtil.isNotBlank(param.getName())) {
                folderService.renameFolder(param.getFolderId(), param.getName(), ConflictModeEnum.THROW_ERR);
            }
            return ResponseEntity.ok().build();
        } catch (FileConflictException e) {
            throw DefaultSaasException.build(e.getMessage()).status(HttpStatus.CONFLICT);
        }
    }

    @PutMapping("/resource/folder")
    @FolderAuthBefore(folderId = "#param.folderId", auth = ResourceAuthEnum.OWN)
    @SysLog(operation = "恢复目录", mode = 3)
    @ApiOperation(value = "恢复目录", notes = "恢复被回收的目录，若提示父目录已被删除，请指定parentId恢复到其他父目录，若提示恢复至的目录存在同名目录，请指定name更新目录名")
    @ApiResponses({
        @ApiResponse(code = 200, message = "请求成功"),
        @ApiResponse(code = 400, message = "请求参数异常/目录信息不存在"),
        @ApiResponse(code = 409, message = "恢复至的目录存在同名目录"),
        @ApiResponse(code = 500, message = "服务器内部错误")
    })
    @ApiOperationSort(4)
    public ResponseEntity<Void> restoreFolder(@Validated @RequestBody HBoxFolderPutParam param) {
        try {
            String userId = SecurityUtils.getUser().getUserId();
            param.setFolderId(HBoxUtil.parseFolderId(userId, param.getFolderId()));
            param.setParentId(HBoxUtil.parseFolderId(userId, param.getParentId()));
            resourceService.restoreResources(null, ListUtil.list(false, param.getFolderId()), param.getParentId(), ConflictModeEnum.THROW_ERR);
            return ResponseEntity.ok().build();
        } catch (FileConflictException e) {
            throw DefaultSaasException.build(e.getMessage()).status(HttpStatus.CONFLICT);
        }
    }

    @DeleteMapping("/resource/folder")
    @FolderAuthBefore(folderId = "#param.folderId", auth = ResourceAuthEnum.OWN)
    @SysLog(operation = "回收/删除目录", mode = 3)
    @ApiOperation(value = "回收/删除目录", notes = "将目录放入回收站, 或彻底删除目录")
    @ApiOperationSort(6)
    public ResponseEntity<Void> deleteFolder(@Validated @RequestBody HBoxFolderDeleteParam param) {
        String userId = SecurityUtils.getUser().getUserId();
        param.setFolderId(HBoxUtil.parseFolderId(userId, param.getFolderId()));
        if (Objects.equals(ResourceStatusEnum.RECYCLED.getCode(), param.getMode())) {
            folderService.recycleFolder(param.getFolderId());
        } else if (Objects.equals(ResourceStatusEnum.DELETED.getCode(), param.getMode())) {
            folderService.deleteFolder(param.getFolderId());
        } else {
            throw DefaultSaasException.build("请求模式异常").status(HttpStatus.BAD_REQUEST);
        }
        return ResponseEntity.ok().build();
    }

    @GetMapping("/resource/archive-space/{spaceId}/folders")
    @SysLog
    @ApiOperation(value = "获取回收站", notes = "获取回收站资源列表")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "spaceId", value = "回收空间ID，10-个人，20-共享", dataType = "String", required = true)
    })
    @ApiOperationSort(7)
    public ResponseEntity<HBoxFolderDTO> getArchivedFolders(@PathVariable String spaceId) {
        String userId = SecurityUtils.getUser().getUserId();
        List<ResourceFolderStructBO> folders = folderService.listUserFolders(userId, HBoxConsts.SPACE_ID,
            LogicQuery.eq(ResourceStatusEnum.RECYCLED.getCode()));
        List<ResourceFileBO> files = fileService.listUserFiles(userId, HBoxConsts.SPACE_ID,
            LogicQuery.eq(ResourceStatusEnum.RECYCLED.getCode()), LogicQuery.none());
        for (ResourceFolderStructBO folder : folders) {
            ResourcesBO subResources = resourceService.listSubResources(folder.getPid(),
                LogicQuery.eq(ResourceStatusEnum.RECYCLED_LINKED.getCode()), -1, true);
            folder.setSubFolders(subResources.getFolders());
            folder.setSubFiles(subResources.getFiles());
        }
        List<HBoxFolderDTO> subFolders = MapperUtil.nf().mapAsList(folders, HBoxFolderDTO.class);
        List<HBoxFileDTO> subFiles = MapperUtil.nf().mapAsList(files, HBoxFileDTO.class);

        HBoxFolderDTO result = new HBoxFolderDTO();
        result.setFolderId(HBoxConsts.ROOT_FOLDER_ID);
        result.setName(HBoxConsts.ROOT_FOLDER_NAME);
        result.setSubFolders(subFolders);
        result.setSubFiles(subFiles);
        return ResponseEntity.ok(result);
    }

    @DeleteMapping("/resource/archive-space/{spaceId}/folders")
    @Transactional(rollbackFor = Exception.class)
    @SysLog
    @ApiOperation(value = "清空回收站", notes = "彻底删除回收站中所有的目录和文件")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "spaceId", value = "回收空间ID，10-个人，20-共享", dataType = "String", required = true)
    })
    @ApiOperationSort(8)
    public ResponseEntity<Void> clearRecycleBin(@PathVariable String spaceId) {
        String userId = SecurityUtils.getUser().getUserId();
        LogicQuery<Integer> status = LogicQuery.eq(ResourceStatusEnum.RECYCLED.getCode()).orEq(ResourceStatusEnum.RECYCLED_LINKED.getCode());
        List<ResourceFolderStructBO> folders = folderService.listUserFolders(userId, HBoxConsts.SPACE_ID, status);
        List<String> folderIds = folders.stream().map(BaseBO::getPid).collect(Collectors.toList());
        List<ResourceFileBO> files = fileService.listUserFiles(userId, HBoxConsts.SPACE_ID, status, LogicQuery.none());
        List<String> fileIds = files.stream().map(BaseBO::getPid).collect(Collectors.toList());

        folderService.deleteFolders(folderIds);
        fileService.deleteFiles(fileIds);
        return ResponseEntity.ok().build();
    }
}
